package com.zhz.system.biz;

import com.beust.jcommander.internal.Lists;
import com.zhz.common.enums.BizResponseEnum;
import com.zhz.common.shiro.entity.UserProfileResponse;
import com.zhz.common.tool.mybatisplus.base.core.contract.Page;
import com.zhz.common.tool.mybatisplus.base.core.contract.PageParam;
import com.zhz.common.utils.ConvertUtils;
import com.zhz.model.system.contract.dto.RoleDTO;
import com.zhz.model.system.contract.dto.UserDTO;
import com.zhz.model.system.contract.dto.UserRoleDTO;
import com.zhz.model.system.contract.param.RoleQueryParam;
import com.zhz.model.system.contract.param.UserQueryParam;
import com.zhz.model.system.contract.param.UserRoleQueryParam;
import com.zhz.system.contract.enums.UserEnableStateEnum;
import com.zhz.system.contract.enums.UserLevelEnum;
import com.zhz.system.contract.request.*;
import com.zhz.system.contract.response.UserFindAllResponse;
import com.zhz.system.contract.response.UserFindOneResponse;
import com.zhz.system.contract.response.UserLoginResponse;
import com.zhz.system.repository.service.RoleService;
import com.zhz.system.repository.service.UserRoleService;
import com.zhz.system.repository.service.UserService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户保存
 *
 * @author zhouhengzhe
 * @date 2023/4/3
 */
@Service
public class UserBizService {

    private final UserService userService;

    private final UserRoleService userRoleService;
    private final RoleService roleService;

    @Autowired
    public UserBizService(UserService userService,
                          UserRoleService userRoleService,
                          RoleService roleService) {
        this.userService = userService;
        this.userRoleService = userRoleService;
        this.roleService = roleService;
    }

    /**
     * 添加用户
     *
     * @param userSaveRequest 保存用户实体
     */
    public void save(UserSaveRequest userSaveRequest) {
        UserDTO userDTO = ConvertUtils.sourceToTarget(userSaveRequest, UserDTO.class);
        String password = new Md5Hash("123456", userDTO.getMobile(), 3).toString();
        userDTO.setLevel(UserLevelEnum.USER.getMessage())
                .setPassword(password)
                .setEnableState(UserEnableStateEnum.ENABLE.getCode());
        userService.save(userDTO);
    }

    /**
     * 更新用户信息
     *
     * @param userUpdateRequest 更新用户实体
     */
    public void update(UserUpdateRequest userUpdateRequest) {
        //1.根据id查询部门
        UserQueryParam userQueryParam = UserQueryParam
                .builder()
                .id(userUpdateRequest.getId())
                .build();
        UserDTO target = userService.getOne(userQueryParam);
        if (Objects.nonNull(target)) {
            target.setUsername(userUpdateRequest.getUsername())
                    .setPassword(userUpdateRequest.getPassword())
                    .setDepartmentId(userUpdateRequest.getDepartmentId())
                    .setDepartmentName(userUpdateRequest.getDepartmentName());
            //更新部门信息
            userService.update(target);
        }
    }


    /**
     * 根据主键ID单个查询用户信息
     *
     * @param userFindOneRequest
     * @return
     */
    public UserFindOneResponse findById(UserFindOneRequest userFindOneRequest) {
        UserDTO userDTO = userService.get(userFindOneRequest.getId());
        BizResponseEnum.USER_NOT_EXIST.assertNotNull(userDTO);
        //获取用户的角色信息
        List<UserRoleDTO> list = userRoleService.list(
                UserRoleQueryParam.builder().userId(userDTO.getId()).build()
        );
        List<UserFindOneResponse.RoleUserResponse> roleUserResponses = list.stream().map(userRoleDTO ->
                UserFindOneResponse.RoleUserResponse
                        .builder()
                        .id(userRoleDTO.getId())
                        .roleId(userRoleDTO.getRoleId())
                        .userId(userRoleDTO.getUserId())
                        .build()).collect(Collectors.toList());
        UserFindOneResponse userFindOneResponse = ConvertUtils.sourceToTarget(userDTO, UserFindOneResponse.class);
        userFindOneResponse.setRoleUserResponse(roleUserResponses);
        return userFindOneResponse;
    }


    /**
     * 指定企业id查询企业的部门列表
     *
     * @param userQueryParam 查询条件
     * @return
     */
    public Page<UserFindAllResponse> page(UserQueryParam userQueryParam) {
        PageParam<UserQueryParam> pageParam = new PageParam<>();
        pageParam.setParam(userQueryParam)
                .setPage(userQueryParam.getPage())
                .setLimit(userQueryParam.getLimit());
        Page<UserDTO> page = userService.page(pageParam);
        List<UserFindAllResponse> userFindAllResponse = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(page.getList())) {
            userFindAllResponse = page.getList().stream().map(userDTO ->
                    ConvertUtils.sourceToTarget(userDTO, UserFindAllResponse.class)).collect(Collectors.toList()
            );
        }
        return Page.succeed(userFindAllResponse, page.getTotal());
    }


    /**
     * 根据id删除部门
     *
     * @param userDeleteRequest 主键ID
     */
    public void deleteById(UserDeleteRequest userDeleteRequest) {
        userService.delete(userDeleteRequest.getId());
    }

    /**
     * 分配角色,插入的时候要考虑差集
     *
     * @param userAssignRoleRequest 分配角色实体
     */
    @Transactional(rollbackFor = Exception.class)
    public void assignRoles(UserAssignRoleRequest userAssignRoleRequest) {
        //检查用户和角色信息是否存在
        beforeCheckUserAndRoleExist(userAssignRoleRequest);
        //查询用户角色信息
        List<UserRoleDTO> userRoleDTOS = userRoleService.list(
                UserRoleQueryParam
                        .builder()
                        .userId(userAssignRoleRequest.getId())
                        .build()
        );
        deleteUnSyncedData(userAssignRoleRequest, userRoleDTOS);
        //差集，数据没有，前端有
        saveUserRole(userAssignRoleRequest, userRoleDTOS);
    }

    /**
     * 保存数据库没有，前端有的
     *
     * @param userAssignRoleRequest 分配角色实体
     * @param userRoleDTOS          用户角色信息
     */
    private void saveUserRole(UserAssignRoleRequest userAssignRoleRequest, List<UserRoleDTO> userRoleDTOS) {
        Set<UserRoleDTO> newUserRoleDTOS = userAssignRoleRequest.getRoleIds()
                .stream()
                .filter(
                        role -> !userRoleDTOS.stream()
                                .map(UserRoleDTO::getRoleId)
                                .collect(Collectors.toList())
                                .contains(role)
                ).map(role -> UserRoleDTO
                        .builder()
                        .roleId(role)
                        .userId(userAssignRoleRequest.getId())
                        .build()).collect(Collectors.toSet());
        //3、设置用户和角色集合的关系
        if (CollectionUtils.isNotEmpty(newUserRoleDTOS)) {
            userRoleService.save(newUserRoleDTOS);
        }
    }

    /**
     * 删除数据没有，前端有的数据
     *
     * @param userAssignRoleRequest 分配角色实体
     * @param userRoleDTOS          用户角色信息
     */
    private void deleteUnSyncedData(UserAssignRoleRequest userAssignRoleRequest, List<UserRoleDTO> userRoleDTOS) {
        //差集（5），数据库有的，前端没有
        Set<String> difference = userRoleDTOS.stream().map(UserRoleDTO::getRoleId).filter(id -> !userAssignRoleRequest.getRoleIds().contains(id)).collect(Collectors.toSet());
        //查询已有的数据
        List<String> ids = userRoleDTOS.stream().filter(userRoleDTO -> difference.contains(userRoleDTO.getRoleId())).map(UserRoleDTO::getId).collect(Collectors.toList());
        //删除数据库差集
        if (CollectionUtils.isNotEmpty(ids)) {
            userRoleService.delete(ids);
        }
    }

    /**
     * 检查用户和角色信息是否存在
     *
     * @param userAssignRoleRequest 分配角色实体
     */
    private void beforeCheckUserAndRoleExist(UserAssignRoleRequest userAssignRoleRequest) {
        //1.根据id查询用户
        UserDTO userDTO = userService.get(userAssignRoleRequest.getId());
        BizResponseEnum.USER_NOT_EXIST.assertNotNull(userDTO);
        //2.设置用户的角色集合，查询已有的用户角色集合
        List<RoleDTO> roleDTOS = roleService.list(
                RoleQueryParam
                        .builder()
                        .ids(new HashSet<>(userAssignRoleRequest.getRoleIds()))
                        .build()
        );
        BizResponseEnum.ROLE_NOT_EXIST.assertNotEmpty(roleDTOS);
    }

    /**
     * 登录
     *
     * @param userLoginRequest 登录信息
     * @return
     */
    public UserLoginResponse login(UserLoginRequest userLoginRequest) {
        String password = userLoginRequest.getPassword();
        String mobile = userLoginRequest.getMobile();
        //try {
            //1.构造登录令牌 UsernamePasswordToken
            //加密密码
            password = new Md5Hash(password, mobile, 3).toString();  //1.密码，盐，加密次数
            UsernamePasswordToken upToken = new UsernamePasswordToken(mobile, password);
            //2.获取subject
            Subject subject = SecurityUtils.getSubject();
            //3.调用login方法，进入realm完成认证
            subject.login(upToken);
            //4.获取sessionId
            String sessionId = (String) subject.getSession().getId();
            return UserLoginResponse
                    .builder()
                    .token(sessionId)
                    .build();
        //} catch (Exception e) {
        //    throw new RuntimeException(BizResponseEnum.MOBILE_OR_PASSWORD_ERROR.getMessage());
        //}
    }

    public static void main(String[] args) {
        System.out.println(new Md5Hash("123456", "13800000001", 3).toString());
    }

    /**
     * 从请求头信息中获取token数据
     * 1.获取请求头信息：名称=Authorization
     * 2.替换Bearer+空格
     * 3.解析token
     * 4.获取clamis
     *
     * @return 用户信息
     */
    public UserProfileResponse profile() {
        //获取session中的安全数据
        Subject subject = SecurityUtils.getSubject();
        //1.subject获取所有的安全数据集合
        PrincipalCollection principals = subject.getPrincipals();
        //2.获取安全数据
        return (UserProfileResponse) principals.getPrimaryPrincipal();
    }
}
